-- icache wrapper
-- this is provided for a place holder until you do the cache labs
-- until then you should just place this file between your fetch stage
-- of your pipeline and your priority mux for main memory.
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;

entity icache is
  port(
    clk       : in  std_logic;
	 memclk	  : in  std_logic;
    nReset    : in  std_logic;

    iMemRead  : in  std_logic;                       -- CPU side
    iMemWait  : out std_logic;                       -- CPU side
    iMemAddr  : in  std_logic_vector (31 downto 0);  -- CPU side
	 iMemData  : out std_logic_vector (31 downto 0);  -- CPU side
    
    state1    : out std_logic_vector(1 downto 0);
    state2    : out std_logic_vector(2 downto 0);
    
    --aiMemWait : in  std_logic;                       -- arbitrator side
    aiMemRead : out std_logic;                       -- arbitrator side
    aiMemAddr : out std_logic_vector (31 downto 0);  -- arbitrator side
    aiMemData : in  std_logic_vector (31 downto 0)   -- arbitrator side
    );

end icache;

architecture struct of icache is
	type validbits is array (0 to 127) of std_logic;
	type physicalAddr is array (0 to 127) of std_logic_vector(20 downto 0);
	type icachedata is array (0 to 127) of std_logic_vector(127 downto 0);
	type fsmstatetype is (srw, sdrw);
	type memstatetype is (ss, srw0, srw1, srw2, srw3, sidle);
	signal refillfsm: memstatetype := sidle;
	signal cachefsm: fsmstatetype := srw;
	signal cachebusy: std_logic;
	signal resetrefillfsm: std_logic;
	signal valid: validbits;
	signal pa: physicalAddr;
	signal data: icachedata;
	signal cacheline: integer;
	signal cachecolumn: integer;
	
begin
	cacheline <= conv_integer(iMemAddr(10 downto 4));
	cachecolumn <= conv_integer(iMemAddr(3 downto 2));	
	with cachefsm select 
	   state1 <= "00" when srw,
	            "01" when others;
	with refillfsm select
	   state2 <= "000" when ss,
	              "001" when srw0,
	              "010" when srw1,
	              "011" when srw2,
	              "100" when srw3,
	              "101" when others;
	state_cachefsm: process(clk)
	begin
		if (rising_edge(clk)) then
			if (nReset = '0') then
					-- Reset here
				for i in 0 to 127 loop
					valid(i) <= '0';
				end loop;
				cachefsm <= srw;
				resetrefillfsm <= '0';				
			else
			case cachefsm is
				when srw =>
					if (iMemRead = '1') then
						if valid(cacheline) = '1' and pa(cacheline) =  iMemAddr (31 downto 11) then
						--hit
							iMemWait <= '0';
							case cachecolumn is
								when 0 => iMemData <= data(cacheline)(31 downto 0);
								when 1 => iMemData <= data(cacheline)(63 downto 32);
								when 2 => iMemData <= data(cacheline)(95 downto 64);
								when others => iMemData <= data(cacheline)(127 downto 96);
							end case;	
							cachefsm <= srw;				
						else
							iMemWait <= '1';
							cachefsm <= sdrw;
							resetrefillfsm <= '1';
							iMemData <= x"00000000";
						end if;				
					else
					   cachefsm <= srw;
					   iMemData <= x"00000000";
					end if;					
				when sdrw =>
					resetrefillfsm <= '0';
					if cachebusy = '1' then
						cachefsm <= sdrw;
						iMemWait <= '1';
						iMemData <= x"00000000";
					else
						pa(cacheline) <= iMemAddr(31 downto 11);
						valid(cacheline) <= '1';						
						case cachecolumn is
							when 0 => iMemData <= data(cacheline)(31 downto 0);
							when 1 => iMemData <= data(cacheline)(63 downto 32);
							when 2 => iMemData <= data(cacheline)(95 downto 64);
							when others => iMemData <= data(cacheline)(127 downto 96);
						end case;
						iMemWait <= '0';
						cachefsm <= srw;
					end if;
			end case;
			end if;
		end if;
	end process;
	
	state_memfsm: process(resetrefillfsm, memclk)
	begin
		if (nReset = '0') then 
			cachebusy <= '0';
			refillfsm <= sidle;
		elsif (resetrefillfsm = '1') then
			cachebusy <= '1';
			refillfsm <= ss;
		elsif (rising_edge(memclk)) then
			case refillfsm is
				when ss =>
					cachebusy <= '1';
					aiMemAddr(31 downto 0) <= iMemAddr(31 downto 4)&"0000";					
					aiMemRead <= '1';
					refillfsm <= srw0;					
				when srw0 =>
					cachebusy <= '1';
					data(cacheline)(31 downto 0) <= aiMemData;
					aiMemAddr(31 downto 0) <= iMemAddr(31 downto 4)&"0100";					
					aiMemRead <= '1';
					refillfsm <= srw1;
				when srw1 =>					
				    cachebusy <= '1';
					data(cacheline)(63 downto 32) <= aiMemData;
					aiMemAddr(31 downto 0) <= iMemAddr(31 downto 4)&"1000";					
					aiMemRead <= '1';
					refillfsm <= srw2;
				when srw2 =>
				    cachebusy <= '1';
					data(cacheline)(95 downto 64) <= aiMemData;					
					aiMemAddr(31 downto 0) <= iMemAddr(31 downto 4)&"1100";					
					aiMemRead <= '1';
					refillfsm <= srw3;
				when srw3 =>					
					data(cacheline)(127 downto 96) <= aiMemData;
					aiMemRead <= '0';
					cachebusy <= '0';
					refillfsm <= sidle;
				when sidle =>
					cachebusy <= '0';
					refillfsm <= sidle;
			end case;
		end if;
	end process;
end struct;